// 专门解决DataGroup的bug！
// data group measureRendererSize 时，会取第一个item来实例化，并且addChild; 然后计算rect
// 这样的做法太tracky了！
// 这导致Tree的第一项是folder时多增加了一次height！
class TreeDataGroup extends eui.DataGroup {
	renderw = 0;

	renderh = 0;

	override measureRendererSize(): void {
		// console.log("TreeDataGroup._measureRendererSize",this.renderw,this.renderh)
		const rect = new egret.Rectangle(0, 0, this.renderw, this.renderh);
		// eslint-disable-next-line dot-notation
		this['setTypicalLayoutRect'](rect);
	}
}

// eslint-disable-next-line dot-notation
// TreeDataGroup.prototype['measureRendererSize'] = TreeDataGroup.prototype._measureRendererSize;

interface TreeViewData {
	content: string;
	arrowicon?: string;
	foldericon?: string;
	fileicon?: string;
	param?: string;
}

class TreeViewItem extends eui.ItemRenderer {
	private rootNode: eui.Group;

	// private bgRect:eui.Rect;
	private itemBgRect: eui.Rect;

	private arrowiconBg: eui.Rect;

	private arrowicon: eui.Image;

	foldericon: eui.Image;

	private content: eui.Label;

	DataGroup: TreeDataGroup;

	private eye: eui.Image;

	private lock: eui.Image;
	// save:eui.Image;

	myHeight = 0;

	public constructor() {
		super();
		this.skinName = 'TreeViewItemSkin';
	}

	protected override childrenCreated(): void {
		super.childrenCreated();
		this.eye.addEventListener(egret.TouchEvent.TOUCH_TAP, this.onTouchEye, this);
		this.lock.addEventListener(egret.TouchEvent.TOUCH_TAP, this.onTouchLock, this);
		// this.save.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onTouchSave,this);

		this.arrowiconBg.addEventListener(egret.TouchEvent.TOUCH_TAP, this.onTouchExpand, this);

		this.foldericon.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.onTouchBeginHold, this);

		this.rootNode.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.onTouchBeginGroup, this);
		this.rootNode.addEventListener(egret.TouchEvent.TOUCH_MOVE, this.onTouchMoveGroup, this);
		this.rootNode.addEventListener(
			egret.TouchEvent.TOUCH_END,
			this.onTouchEndGroup,
			this,
			false,
			1000,
		);

		// 先捕获，再冒泡
		// 并且先由stage --> target (not target)的次序捕获
		// 再由target --> stage冒泡
		// 而scroll捕获了最外层的onTouchEnd，并且stopPropagation
		this.rootNode.addEventListener(mouse.MouseEvent.MOUSE_OVER, this.onMouseOver, this);
		this.rootNode.addEventListener(mouse.MouseEvent.MOUSE_OUT, this.onMouseOut, this);
		// this.addEventListener(egret.Event.ENTER_FRAME,this.update,this);

		this.arrowicon.anchorOffsetX = this.arrowicon.width;
		this.arrowicon.anchorOffsetY = this.arrowicon.height;

		// this.rootNode.touchEnabled = false;
		this.arrowicon.touchEnabled = false;
		this.foldericon.touchEnabled = true;
		this.rectLine.touchEnabled = false;
		this.content.touchEnabled = false;
		this.itemBgRect.touchEnabled = false;
	}

	tree: TreeView = null;

	option: TreeOption;

	protected defaultOptData(): void {
		if (this.data.arrowicon == null) {
			this.data.arrowicon = this.option.arrowicon;
		}
		if (this.data.foldericon == null) {
			this.data.foldericon = this.isFolder() ? this.option.foldericon : this.option.fileicon;
		}
		if (this.data.content == null) {
			this.data.content = '';
		}
		if (this.data.itemHeight == null) {
			this.data.itemHeight = this.option.itemHeight;
		}
		if (this.data.fontSize == null) {
			this.data.fontSize = this.option.fontSize;
		}
	}

	protected override dataChanged(): void {
		this.option = this.data.__option;
		this.tree = this.data.__tree;
		this.data.__item = this;

		egret.assert(this.tree instanceof TreeView);

		this.defaultOptData();

		this.arrowicon.source = this.data.arrowicon;
		this.content.text = this.getDisplay();

		// 高度
		this.myHeight = this.data.itemHeight;

		this.height = this.myHeight;
		this.rootNode.height = this.myHeight;
		this.itemBgRect.height = this.myHeight;

		// 背景颜色
		// this.itemBgRect.fillColor = this.data.bgColor ? this.data.bgColor : this.option.bgColor;
		this.itemBgRect.fillColor = this.option.selectedColor;
		this.itemBgRect.visible = this.data.selected;
		// this.bgRect.fillColor = this.data.bgColor ? this.data.bgColor : this.option.bgColor;

		// let tab = this.option.tab || 50
		this.left = 0;
		this.right = 0;

		if (this.parent) {
			this.width = this.parent.width;
		} // - this.x;
		// this.itemBgRect.width = this.width //- 10;

		// 字体大小
		this.content.size = this.data.fontSize;

		this.lock.source = this.data.lock ? 'lock_png' : 'unlock_png';
		this.eye.source = this.data.hide ? 'uneye_png' : 'eye_png';

		if (!this.isFolder()) {
			this.foldericon.source = this.data.foldericon; // 当this.data.foldericon无效就会显示dir的图标！
			this.arrowicon.visible = false;
			this.arrowiconBg.visible = false;
			const { x } = this.foldericon;
			this.foldericon.x -= x;
			this.content.x -= x;
			if (this.data.foldericon === '') {
				this.content.x -= this.foldericon.width;
			}
		} else {
			this.foldericon.source = this.data.foldericon.normal;
			// this.arrowicon.visible = this.getItems().length > 0
			this.arrowicon.visible = true;
			// this.data.expand = false
			if (this.isExpand()) {
				this.expand(true);
			} else {
				this.arrowicon.rotation = -90;
			}
		}
	}

	setSelected(sel: boolean): void {
		// this.itemBgRect.fillColor = (sel ? this.option.selectedColor : this.option.bgColor) ;
		this.data.selected = sel;
		this.itemBgRect.visible = sel;
		if (!this.isFolder()) {
			this.lock.source = this.data.lock ? 'lock_png' : 'unlock_png';
			this.eye.source = this.data.hide ? 'uneye_png' : 'eye_png';
			if (this.option.useLock) {
				this.lock.visible = sel;
			}
			if (this.option.useEye) {
				this.eye.visible = sel;
			}
		} else {
			// this.save.visible = sel && this.data.save
		}
	}

	isSelected(): boolean {
		// return this.itemBgRect.fillColor == this.option.selectedColor
		// return this.itemBgRect.visible
		return this.data.selected;
	}

	getItems(): TreeItemData[] {
		return this.data.items;
	}

	getItemsById(id: number): TreeItemData {
		for (let i = 0; i < this.getItems().length; i++) {
			if (id === this.getItems()[i].id) {
				return this.getItems()[i];
			}
		}
		return null;
	}

	isFolder(): boolean {
		return Array.isArray(this.getItems());
	}

	isOpen(): boolean {
		return this.arrowicon.rotation === 0;
	}

	private popEvent(): void {
		this.dispatchEventWith(TreeView.clearSelectEvent, true, this);
		this.tree.doSelect(this);
		this.dispatchEventWith(TreeView.tapEvent, true, this);
	}

	rectLine: eui.Rect;

	// rectLinePos:string;
	getDropPos(y: number): string {
		if (this.isFolder() && Math.abs(y - this.myHeight / 2) < this.myHeight / 2 - 5) {
			// all is 40!
			return 'middle';
		}
		return y > this.myHeight / 2 ? 'down' : 'up';
	}

	showRedLine(y: number): void {
		const pos = this.getDropPos(y);
		const { height } = this.rectLine;
		if (pos === 'middle') {
			this.rectLine.y = this.myHeight / 2 - height / 2;
		} else if (pos === 'down') {
			this.rectLine.y = this.myHeight - height / 2;
		} else {
			this.rectLine.y = -height / 2;
		}
		this.rectLine.visible = true;
	}

	hideRedLine(): void {
		this.rectLine.visible = false;
	}

	private onTouchMoveGroup(ev: egret.TouchEvent): void {
		// console.log("onTouchMoveGroup",ev.localX,ev.localY,this.myHeight,this.getDisplay(),ev.target)
		const { cursor } = getCursor();
		if (cursor && cursor instanceof TreeViewItem) {
			this.showRedLine(ev.localY);
		}
	}

	// when drag tree,this msg will swallow by scroll!
	// 事件会被scroller的事件冲掉！
	private onTouchEndGroup(event: egret.TouchEvent): void {
		// some thing drag in
		const { cursor } = getCursor();
		if (cursor && cursor instanceof TreeViewItem) {
			const pos = this.getDropPos(event.localY);
			this.onDrop(cursor, pos);
		}
	}

	// 向上或者向下一层！
	indexAdd(add: boolean): void {
		const father = this.getParentItem();
		if (!father) {
			return;
		}
		let index = father.data.items.indexOf(this.data);
		if (add) {
			index++;
			if (index >= father.data.items.length) {
				return;
			}
		} else {
			index--;
			if (index < 0) {
				return;
			}
		}
		father.newIndex(this, index);
	}

	clearDataParam(data: any): any {
		const clear = {
			[data]: data.__item,
		};

		data.__option = null;
		data.__tree = null;
		data.__item = null;
		function fillopt(objs: any[]): void {
			for (let i = 0; i < objs.length; i++) {
				const obj = objs[i];
				obj.__option = null;
				obj.__tree = null;
				clear[obj] = obj.__item;
				obj.__item = null;
				if (obj.items) {
					fillopt(obj.items);
				}
			}
		}
		if (data.items) {
			fillopt(data.items);
		}

		return clear;
	}

	restoreDataParam(data: any, clear: any): void {
		const option = this.data.__option;
		const tree = this.data.__tree;

		data.__option = option;
		data.__tree = tree;
		data.__item = clear[data];
		function fillopt(objs: any[]): void {
			for (let i = 0; i < objs.length; i++) {
				const obj = objs[i];
				obj.__option = option;
				obj.__tree = tree;
				obj.__item = clear[obj];
				if (obj.items) {
					fillopt(obj.items);
				}
			}
		}
		if (data.items) {
			fillopt(data.items);
		}
	}

	onDrop(item: TreeViewItem, pos: string): void {
		// console.log("onDrop",item,pos)
		if (item.tree === this.tree) {
			// 如果自己是folder，放在child末尾
			// 如果自己是file，放在后面
			const itemintree = this.tree.findData(item.data);
			if (itemintree) {
				if (isSelfOrParent(this, itemintree)) {
					ui.msgerror('不能把父亲拖到儿子下面');
					return;
				}

				const father = this.getParentItem();
				let { data } = item;
				if (isShiftDown()) {
					const clear = this.clearDataParam(item.data);
					data = Utils.deepCopy(item.data);

					this.restoreDataParam(item.data, clear);
					this.restoreDataParam(data, {});
				} else {
					itemintree.removeSelf();
				}

				if ((this.isFolder() && pos === 'middle') || !father) {
					this.getItems().unshift(data);
					this.onChanged(true);
				} else {
					father.addItemPos(this, data, pos === 'up');
				}
			}
		}
	}

	private onTouchBeginGroup(event: egret.TouchEvent): void {
		this.popEvent();
		event.stopPropagation();
		// if (this.option.drag){
		// 	console.log("addHoldDrag")
		// 	MainWindow.ins().hold.addHoldDrag(300,this.onHold,this)
		// }
	}

	private onTouchBeginHold(event: egret.TouchEvent): void {
		if (this.option.drag) {
			// console.log("addHoldDrag")
			MainWindow.ins().hold.addHoldDrag(300, this.onHold, this);
		}
	}

	onHold(): void {
		// hold 一定时间，改变鼠标
		// console.log("TreeViewItem.onHold")
		const item = new TreeViewItem();
		item.data = this.data;
		item.dataChanged();
		// item.touchEnabled = false;
		// item.touchChildren = false;
		// ui.msgline("进入dragtreeitem")
		this.tree.onHoldStart();

		MainWindow.ins().mode.changeMode(
			'dragtreeitem',
			{ escExit: true, touchendExit: true },
			() => {
				// ui.msgline("退出dragtreeitem")
				getCursor().clearCursor('dragtreeitem');
				this.tree.onHoldEnd();
			},
		);
		getCursor().setCursor('dragtreeitem', item);
	}

	isExpand(): boolean {
		return this.data.expand;
	}

	filterItem(i: TreeItemData, filter: TreeFilterType): boolean {
		if (i.items) {
			return true;
		}
		if (typeof filter === 'string') {
			return i.content.indexOf(filter) !== -1;
		}
		return filter(i);
	}

	filterItemData(): TreeItemData[] {
		if (!this.option.filter) {
			return this.getItems();
		}
		const items = [];
		for (const i of this.getItems()) {
			if (this.filterItem(i, this.option.filter)) {
				items.push(i);
			}
		}
		return items;
	}

	expand(updateheight: boolean): void {
		if (!this.isFolder()) {
			return;
		}
		if (this.DataGroup) {
			return;
		}
		this.data.expand = true;

		this.arrowicon.rotation = 0;
		this.foldericon.source = this.data.foldericon.press;

		const items = this.filterItemData();

		// updateheight = false
		if (updateheight) {
			this.setNodeHeight(this.myHeight, this.myHeight * items.length);
		} else {
			this.height = this.myHeight + this.myHeight * items.length;
		}

		this.DataGroup = new TreeDataGroup();
		this.DataGroup.renderw = this.width;
		this.DataGroup.renderh = this.myHeight;
		this.DataGroup.useVirtualLayout = false; // 编辑器，不对显示优化了

		this.DataGroup.y = this.myHeight;
		const tab = this.option.tab || 50;
		// this.DataGroup.x = tab;
		this.DataGroup.left = tab;
		this.DataGroup.right = 0;

		this.addChild(this.DataGroup);

		// console.log("expand items",this.getContent(),items.length)
		this.DataGroup.dataProvider = new eui.ArrayCollection(items);
		this.DataGroup.itemRenderer = TreeViewItem;
	}

	unexpand(): void {
		if (!this.isFolder()) {
			return;
		}
		if (!this.DataGroup) {
			return;
		}
		this.data.expand = false;
		this.arrowicon.rotation = -90;
		// this.foldericon.source = this.data.foldericon.normal;

		this.removeChild(this.DataGroup);
		this.DataGroup = null;

		// this.height = this.myHeight;
		this.setNodeHeight(this.myHeight, 0);
	}

	// 不知道这个逻辑是否正确。。。
	expandheight = 0;

	setNodeHeight(myheight: number, expandheight: number): void {
		this.height = myheight + expandheight;
		if (this.expandheight === expandheight) {
			return;
		}

		const delta = expandheight - this.expandheight;
		this.expandheight = expandheight;

		// console.log(`setNodeHeight ${this.getContent()} expandheight${expandheight} delta${delta}`)
		// console.trace()

		// return
		let now = this.parent;
		while (now) {
			if (now instanceof TreeViewItem) {
				now.height += delta;
				// now.updateHeight() 因为此时还未添加为child！
				// console.log(now.getContent(),now.height,delta)
				if (this.isFolder()) {
					now.expandheight += delta;
				}
			}
			if (now instanceof TreeView) {
				break;
			}
			now = now.parent;
		}
	}

	updateHeight(): void {
		let h = 0;
		for (const item of this.DataGroup.$children) {
			h += item.height;
		}
		this.height = this.myHeight + h;
	}

	private onTouchEye(event: egret.TouchEvent): void {
		this.data.hide = !this.data.hide;
		this.eye.source = this.data.hide ? 'uneye_png' : 'eye_png';
		this.dispatchEventWith(TreeView.eyeEvent, true, this);
	}

	private onTouchLock(event: egret.TouchEvent): void {
		this.data.lock = !this.data.lock;
		this.lock.source = this.data.lock ? 'lock_png' : 'unlock_png';
		this.dispatchEventWith(TreeView.lockEvent, true, this);
	}
	// private onTouchSave(event:egret.TouchEvent){
	//	this.dispatchEventWith(TreeView.saveEvent,true,this);
	// }

	private onTouchExpand(event: egret.TouchEvent): void {
		this.popEvent();
		event.stopPropagation();

		if (!this.isFolder()) {
			return;
		}

		if (this.isExpand()) {
			this.unexpand();
		} else {
			this.expand(true);
		}
	}

	expandNextFrame(): void {
		const root = this.getFirst();
		if (root) {
			// console.log("expandNextFrame",this.tree)
			this.tree.selectItem(root);
			root.expand(true);
		}
	}

	expandFirst(): void {
		// calllater不好使，必须nextFrame
		nextFrame(this.expandNextFrame, this);
	}

	getFirst(): TreeViewItem {
		if (this.DataGroup && this.DataGroup.numElements > 0) {
			const obj = this.DataGroup.getElementAt(0);
			if (obj instanceof TreeViewItem) {
				return obj;
			}
		}
		return null;
	}

	newIndex(item: TreeViewItem, index: number): void {
		const old = this.getItems().indexOf(item.data);
		egret.assert(old >= 0);
		this.getItems().splice(old, 1);
		this.getItems().insert(index, item.data);
		this.onChanged(true);
	}

	addItemPos(item: TreeViewItem, data: any, up: boolean): void {
		let index = this.getItems().indexOf(item.data);
		egret.assert(index >= 0);
		if (!up) {
			index++;
		}
		this.getItems().insert(index, data);
		this.onChanged(true);
	}

	addItem(
		after: number,
		id: number,
		content: string,
		param: string,
		foldericon: string,
		focus = false,
	): TreeItemData {
		if (!this.isFolder()) {
			return null;
		}
		const data: TreeItemData = {
			id,
			content,
			param,
			foldericon,
			__option: this.option,
			__tree: this.tree,
			// type,
			selected: true,
		};
		return this._addItem(after, data, focus);
	}

	// clearSelect():void{
	// 	function clearTreeItem(objs: egret.DisplayObjectContainer): void {
	// 		for (const c of objs.$children) {
	// 			if (c instanceof TreeViewItem) {
	// 				c.setSelected(false);
	// 			}
	// 			if (c instanceof egret.DisplayObjectContainer) {
	// 				clearTreeItem(c);
	// 			}
	// 		}
	// 	}
	// 	clearTreeItem(this);
	// }

	_addItem(after: number, data: TreeItemData, focus: boolean): TreeItemData {
		if (data.selected) {
			for (const data2 of this.getItems()) {
				data2.selected = false;
			}
			if (focus) {
				this.setFocusLater();
			}
		}

		if (after === -1 || after >= this.getItems().length) {
			this.getItems().push(data);
		} else {
			this.getItems().insert(after + 1, data);
		}
		this.onChanged(true);
		return data;
	}

	addFolder(after: number, id: number, content: string, focus = false): TreeItemData {
		if (!this.isFolder()) {
			return null;
		}
		const data: TreeItemData = {
			id,
			content,
			items: [],
			__option: this.option,
			__tree: this.tree,
		};
		return this._addItem(after, data, focus);
	}

	onChanged(forceexpand = true): void {
		// this.arrowicon.visible = this.getItems().length > 0
		if (this.isExpand()) {
			this.unexpand();
			if (this.getItems().length > 0) {
				this.expand(true);
			}
		} else if (forceexpand) {
			if (this.getItems().length > 0) {
				this.expand(true);
			}
		}
	}

	getParentItem(): TreeViewItem {
		let item = this.parent;
		while (item) {
			if (item instanceof TreeViewItem) {
				return item;
			}
			item = item.parent;
		}
		return null;
	}

	removeSelf(): void {
		const item = this.getParentItem();
		if (item) {
			item.removeItem(this);
		}
	}

	setFocusLater(): void {
		this.once(egret.Event.ENTER_FRAME, this.setFocus, this);
	}

	setFocus(): void {
		const item = this.getSelectedItem();
		if (item) {
			MainWindow.ins().focus.setFocusTarget(item);
		}
	}

	public getSelectedItem(): TreeViewItem {
		function getTreeItem(objs: egret.DisplayObjectContainer): TreeViewItem {
			for (const c of objs.$children) {
				if (c instanceof TreeViewItem) {
					if (c.isSelected()) {
						return c;
					}
				}
				if (c instanceof egret.DisplayObjectContainer) {
					const rt = getTreeItem(c);
					if (rt) {
						return rt;
					}
				}
			}
			return null;
		}
		return getTreeItem(this);
	}

	removeItem(item: TreeViewItem): boolean {
		if (!this.isFolder()) {
			return false;
		}
		const index = item.getIndex();
		const num = this.getItems().removeIf((a) => a === item.data);
		if (num > 0) {
			if (item.data.selected) {
				const prev = index > 0 ? index - 1 : index;
				if (this.getItems().length > prev) {
					this.getItems()[prev].selected = true;
					this.setFocusLater();
				}
			}
			this.onChanged(false);
			return true;
		}
		return false;
	}

	onMouseOver(evt: egret.TouchEvent): void {
		// console.log("onMouseOver",evt.localX,evt.localY,this.getDisplay())
		this.content.textColor = COLOR_LABEL_OVER;
	}

	onMouseOut(evt: egret.TouchEvent): void {
		// console.log("onMouseOver",evt.localX,evt.localY,this.getDisplay())
		this.content.textColor = COLOR_LABEL_NORMAL;
		this.hideRedLine();
	}

	getDisplay(): string {
		// let format = this.option.displayFormat; //"%1(%2)"
		// if (this.data.param || this.data.param === 0){
		if (!miscUtils.isStringnull(this.data.param)) {
			// let paramname = format.paramname;
			return `${this.getContent()}(${this.data.param})`;
		}
		return this.getContent();
	}

	setIcon(file: string): void {
		this.data.foldericon = file || this.option.fileicon;
		this.foldericon.source = this.data.foldericon;
	}

	setText(text: string): void {
		this.data.content = text;
		this.content.text = this.getDisplay();
	}

	setId(id: number): void {
		this.data.id = id;
		this.content.text = this.getDisplay();
	}

	setParam(param: string): void {
		this.data.param = param;
		this.content.text = this.getDisplay();
	}

	setDisplay(text: string, param: string): void {
		this.data.content = text;
		this.data.param = param;
		this.content.text = this.getDisplay();
	}

	getText(): string {
		return this.content.text;
	}

	getContent(): string {
		return this.data.content;
	}

	getParam(): string {
		return this.data.param;
	}

	updateHideLock(): void {
		this.lock.source = this.data.lock ? 'lock_png' : 'unlock_png';
		this.eye.source = this.data.hide ? 'uneye_png' : 'eye_png';
	}

	getJsonData(): any {
		function item2obj(data: any): any {
			if (data.items) {
				const items = [];
				for (const d of data.items) {
					items.push(item2obj(d));
				}
				return { [data.content]: items };
			}
			const d = data.datas || {};
			d.content = data.content;
			return d;
		}
		return item2obj(this.data);
	}

	getData(): any {
		function item2obj(data: any): any {
			const j: any = {
				id: data.id,
				content: data.content,
				datas: data.datas,
			};
			if (data.items) {
				j.items = [];
				for (const d of data.items) {
					j.items.push(item2obj(d));
				}
			}
			return j;
		}
		return item2obj(this.data);
	}

	// 自己由json生成的！
	updateJsonValue(value: string): void {
		this.tree.updateItemJson(this, value);
		this.setParam(value);
	}

	getChildItem(key: string): TreeViewItem {
		if (!this.DataGroup) {
			return null;
		}
		for (const c of this.DataGroup.$children) {
			if (c instanceof TreeViewItem) {
				if (c.getContent() === key) {
					return c;
				}
			}
		}
		return null;
	}

	findItemByKeys(key: string): TreeViewItem {
		const keys = key.split('.');
		let item: TreeViewItem = this;
		while (keys.length > 0) {
			if (item == null) {
				return null;
			}
			item = item.getChildItem(keys.shift());
		}
		return item;
	}

	getIndex(): number {
		if (!this.parent) {
			return -1;
		}
		return this.parent.getChildIndex(this);
	}
}
